﻿
// MIT License

// Copyright (c) 2020 Tsukanov Alexander

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// Lasciate ogni speranza, voi ch'entrate.
// Внимание! Это просто proof of concept и попытка мимикрировать под компилятор платформы.
// Если что-то кажется странным, то это не кажется. Странное сознательно делалось ради мимикрии.
// Попытка повторить компилятор платформы оказалась для автора единственным развлечением в этом унылом занятии.
// Генератор ничего интересного сам по себе не представляет. Сложность на уровне курсовой работы в универе.
// Использовать можно разве что в учебных целях.

// Разработка велась с использованием материалов https://github.com/EvilBeaver/v8asm

Перем Типы;
Перем Токены;
Перем Исходник;
Перем ТаблицаТокенов;

Перем РазделКоманд;
Перем РазделКонстант;
Перем РазделПеременных;
Перем РазделМетодов;
Перем РазделПеременныхМетода;

Перем ПеременныеМетодов; // структура
Перем ЗначенияПараметровМетодовПоУмолчанию; // структура

Перем КомандыЗагрузкиПеременных;
Перем КомандыБинарныхОпераций;

Перем Команды;
Перем ВстроенныеФункции;

Перем КэшКонстант; // соответствие
Перем КэшПеременных; // соответствие
Перем КэшМетодов; // соответствие
Перем КэшВременныхПеременных; // соответствие
Перем Возвраты; // массив
Перем Метки; // соответствие
Перем ПереходыНаМетки; // соответствие
Перем ПереходыНаКонецМодуля; // массив

Перем ФиктивныеОбъявления; // соответствие

Перем СтекПереходовВЦиклах;

Перем ЭтоТелоМодуля;

Перем ФлагиПеременных;
Перем ФлагиМетодов;

Перем ТочкаВходаМодуля;
Перем ТочкиВходаМетодов;

Процедура Открыть(Парсер)

	Типы = Парсер.Типы();
	Токены = Парсер.Токены();
	Исходник = Парсер.Исходник();
	ТаблицаТокенов = Парсер.ТаблицаТокенов();

	РазделКоманд.Очистить();
	РазделКонстант.Очистить();
	РазделПеременных.Очистить();
	РазделМетодов.Очистить();
	РазделПеременныхМетода.Очистить();

	ПеременныеМетодов.Очистить();
	ЗначенияПараметровМетодовПоУмолчанию.Очистить();

	КомандыЗагрузкиПеременных.Очистить();

	КэшКонстант.Очистить();
	КэшПеременных.Очистить();
	КэшМетодов.Очистить();
	КэшВременныхПеременных.Очистить();
	Возвраты.Очистить();
	Метки.Очистить();
	ПереходыНаМетки.Очистить();
	ПереходыНаКонецМодуля.Очистить();

	ФиктивныеОбъявления.Очистить();

	СтекПереходовВЦиклах.Очистить();

	ЭтоТелоМодуля = Ложь;

	КомандыБинарныхОпераций[Токены.И] = Команды.And;
	КомандыБинарныхОпераций[Токены.Или] = Команды.Or;
	КомандыБинарныхОпераций[Токены.ЗнакСложения] = Команды.Add;
	КомандыБинарныхОпераций[Токены.ЗнакВычитания] = Команды.Sub;
	КомандыБинарныхОпераций[Токены.ЗнакУмножения] = Команды.Mul;
	КомандыБинарныхОпераций[Токены.ЗнакДеления] = Команды.Div;
	КомандыБинарныхОпераций[Токены.ЗнакОстатка] = Команды.Mod;
	КомандыБинарныхОпераций[Токены.ЗнакРавно] = Команды.Cmp;
	КомандыБинарныхОпераций[Токены.ЗнакНеРавно] = Команды.Ne;
	КомандыБинарныхОпераций[Токены.ЗнакМеньше] = Команды.Lt;
	КомандыБинарныхОпераций[Токены.ЗнакБольше] = Команды.Gt;
	КомандыБинарныхОпераций[Токены.ЗнакМеньшеИлиРавно] = Команды.Lte;
	КомандыБинарныхОпераций[Токены.ЗнакБольшеИлиРавно] = Команды.Gte;

КонецПроцедуры

Функция Закрыть()

	// осторожно, тут лютый фарш
	// мне лень делать это красиво, да и смысла пока не вижу
	// цель была только мимикрировать под платформу с минимальными усилиями

	Результат = Новый Массив;

	// Команды

	Результат.Добавить(СтрШаблон("{""Cmd"",%1,%2," "", Сериализовать(РазделКоманд.Количество()), Сериализовать(ТочкаВходаМодуля)));

	СписокКоманд = Новый Массив;
	Для Каждого Элемент Из РазделКоманд Цикл
		СписокКоманд.Добавить(СериализоватьСписок(Элемент));
	КонецЦикла;
	Результат.Добавить(СтрСоединить(СписокКоманд, "," + Символы.ПС));

	Результат.Добавить("" "}");

	// Константы

	Если РазделКонстант.Количество() > 0 Тогда

	Результат.Добавить(СтрШаблон("," "{""Const"",%1," "", Сериализовать(РазделКонстант.Количество())));
	СписокКонстант = Новый Массив;
	Для Каждого Элемент Из РазделКонстант Цикл
		Константа = Новый Массив;
		Если ТипЗнч(Элемент) = Тип("Строка") Тогда
			Константа.Добавить("S");
		ИначеЕсли ТипЗнч(Элемент) = Тип("Число") Тогда
			Константа.Добавить("N");
		ИначеЕсли ТипЗнч(Элемент) = Тип("Дата") Тогда
			Константа.Добавить("D");
		ИначеЕсли ТипЗнч(Элемент) = Тип("Неопределено") Тогда
			Константа.Добавить("U");
		ИначеЕсли ТипЗнч(Элемент) = Тип("Булево") Тогда
			Константа.Добавить("B");
		Иначе
			Константа.Добавить("");
		КонецЕсли;
		Константа.Добавить(Элемент);
		СписокКонстант.Добавить(СериализоватьСписок(Константа));
	КонецЦикла;
	Результат.Добавить(СтрСоединить(СписокКонстант, "," + Символы.ПС));

	Результат.Добавить("" "}");

	КонецЕсли;

	// Переменные

	Если РазделПеременных.Количество() > 0 Тогда

		Результат.Добавить(СтрШаблон("," "{""Var"",%1," "", Сериализовать(РазделПеременных.Количество())));

		СписокПеременных = Новый Массив;
		Для Каждого Элемент Из РазделПеременных Цикл
			СписокПеременных.Добавить(СериализоватьСписок(Элемент));
		КонецЦикла;
		Результат.Добавить(СтрСоединить(СписокПеременных, "," + Символы.ПС));

		Результат.Добавить("" "}");

	КонецЕсли;

	// Методы

	Если РазделМетодов.Количество() > 0 Тогда

		Результат.Добавить(СтрШаблон("," "{""Proc"",%1," "", Сериализовать(РазделМетодов.Количество())));

		СписокМетодов = Новый Массив;
		Для Каждого ОписаниеМетода Из РазделМетодов Цикл

			ТочкаВходаМетода = Неопределено;
			Если ТочкиВходаМетодов.Свойство(ОписаниеМетода[0], ТочкаВходаМетода) Тогда
				ОписаниеМетода[3] = ТочкаВходаМетода;
			КонецЕсли;

			Метод = Новый Массив;

			ДобавлятьПеренос = Ложь;

			Метод.Добавить("{");

			Для Каждого Элемент Из ОписаниеМетода Цикл
				Метод.Добавить(Сериализовать(Элемент));
				Метод.Добавить(",");
			КонецЦикла;
			Метод.Удалить(Метод.ВГраница());

			// Переменные метода

			РазделПеременныхМетода = Неопределено;

			Если ПеременныеМетодов.Свойство(ОписаниеМетода[0], РазделПеременныхМетода)
				И РазделПеременныхМетода.Количество() > 0 Тогда

				Метод.Добавить(СтрШаблон("," "{""Var"",%1," "", Сериализовать(РазделПеременныхМетода.Количество())));

				СписокПеременных = Новый Массив;
				Для Каждого Элемент Из РазделПеременныхМетода Цикл
					СписокПеременных.Добавить(СериализоватьСписок(Элемент));
				КонецЦикла;
				Метод.Добавить(СтрСоединить(СписокПеременных, "," + Символы.ПС));

				Метод.Добавить("" "}");

				ДобавлятьПеренос = Истина;

			КонецЕсли;

			// Значения параметров по умолчанию

			ЗначенияПараметровПоУмолчанию = Неопределено;

			Если ЗначенияПараметровМетодовПоУмолчанию.Свойство(ОписаниеМетода[0], ЗначенияПараметровПоУмолчанию)
				И ЗначенияПараметровПоУмолчанию.Количество() > 0 Тогда

				Метод.Добавить(СтрШаблон("," "{""DefPrm"",%1," "", Сериализовать(ЗначенияПараметровПоУмолчанию.Количество())));

				СписокЗначенийПараметров = Новый Массив;
				Для Каждого Элемент Из ЗначенияПараметровПоУмолчанию Цикл
					ЗначениеПараметра = Новый Массив;
					Если ТипЗнч(Элемент) = Тип("СтрокаТаблицыЗначений")
						И Элемент.Тип = Типы.ВыражениеЛитерал Тогда
						Если ТипЗнч(Элемент.Значение) = Тип("Строка") Тогда
							ЗначениеПараметра.Добавить("S");
						ИначеЕсли ТипЗнч(Элемент.Значение) = Тип("Число") Тогда
							ЗначениеПараметра.Добавить("N");
						ИначеЕсли ТипЗнч(Элемент.Значение) = Тип("Дата") Тогда
							ЗначениеПараметра.Добавить("D");
						ИначеЕсли ТипЗнч(Элемент.Значение) = Тип("Неопределено") Тогда
							ЗначениеПараметра.Добавить("U");
						ИначеЕсли ТипЗнч(Элемент.Значение) = Тип("Булево") Тогда
							ЗначениеПараметра.Добавить("B");
						Иначе
							ЗначениеПараметра.Добавить("");
						КонецЕсли;
					ИначеЕсли ТипЗнч(Элемент) = Тип("СтрокаТаблицыЗначений")
						И Элемент.Тип = Типы.ВыражениеСтроковое Тогда
						СписокЧастей = Новый Массив;
						Для Каждого Часть Из Элемент.Элементы Цикл
							СписокЧастей.Добавить(Часть.Значение);
						КонецЦикла;
						ЗначениеПараметра.Добавить("S");
						ЗначениеПараметра.Добавить(СтрСоединить(СписокЧастей, Символы.ПС));
					Иначе
						ЗначениеПараметра.Добавить("");
					КонецЕсли;
					Если Элемент <> Неопределено
						И Элемент.Тип <> Типы.ВыражениеСтроковое
						И Элемент.Значение <> Неопределено Тогда
						ЗначениеПараметра.Добавить(Элемент.Значение);
					КонецЕсли;
					СписокЗначенийПараметров.Добавить(СериализоватьСписок(ЗначениеПараметра));
				КонецЦикла;

				Метод.Добавить(СтрСоединить(СписокЗначенийПараметров, "," + Символы.ПС));

				Метод.Добавить("" "}");

				ДобавлятьПеренос = Истина;

			КонецЕсли;

			Если ДобавлятьПеренос Тогда
				Метод.Добавить(Символы.ПС);
			КонецЕсли;
			Метод.Добавить("}");

			СписокМетодов.Добавить(СтрСоединить(Метод));

		КонецЦикла;
		Результат.Добавить(СтрСоединить(СписокМетодов, "," + Символы.ПС));

		Результат.Добавить("" "}");

	КонецЕсли;

	Возврат СтрШаблон("{1," "%1" "}", СтрСоединить(Результат));

КонецФункции

Функция СериализоватьСписок(Список)

	Результат = Новый Массив;

	Для Каждого Элемент Из Список Цикл
		Результат.Добавить(Сериализовать(Элемент));
	КонецЦикла;

	Возврат СтрШаблон("{%1}", СтрСоединить(Результат, ","));

КонецФункции

Функция Сериализовать(Элемент)

	Если ТипЗнч(Элемент) = Тип("Строка") Тогда
		Возврат """" + СтрЗаменить(Элемент, """", """""") + """";
	КонецЕсли;

	Возврат Формат(Элемент, "ЧРД=.; ЧН=0; ЧГ=; ЧО=1; ДФ=yyyyMMddhhmmss; ДП=00010101000000; БЛ=0; БИ=1");

КонецФункции

Функция Посетить(Парсер, Модуль) Экспорт

	Открыть(Парсер);

	ПосетитьОбъявления(Модуль.Объявления);
	ЭтоТелоМодуля = Истина;
	ТочкаВходаМодуля = ИндексСледующейКоманды();
	ПосетитьОператоры(Модуль.Операторы);
	ЭтоТелоМодуля = Ложь;
	ВыдатьКоманду(Команды.End);
	Если Модуль.Операторы.Количество() = 0 Тогда
		ВыдатьКоманду(Команды.End); // без понятия что за ересь, но так делает платформа
	КонецЕсли;
	Для Каждого ИндексПерехода Из ПереходыНаКонецМодуля Цикл
		ВставитьКоманду(ИндексПерехода, Команды.Jmp, ИндексТекущейКоманды());
	КонецЦикла;
	ВставитьПереходыНаМетки();

	Возврат Закрыть();

КонецФункции // Посетить()

Процедура ПосетитьОбъявления(Объявления)
	Для Каждого Объявление Из Объявления Цикл
		ПосетитьОбъявление(Объявление);
	КонецЦикла;
КонецПроцедуры // ПосетитьОбъявления()

Процедура ПосетитьОператоры(Операторы)
	Для Каждого Оператор Из Операторы Цикл
		Если Оператор.Тип <> Типы.ОператорПопытка
			И Оператор.Тип <> Типы.ОператорМетка Тогда
			ВыдатьКоманду(Команды.LineNum, Оператор.Начало.НомерСтроки);
		КонецЕсли;
		ПосетитьОператор(Оператор);
		Если Оператор.Тип <> Типы.ОператорПопытка
			И Оператор.Тип <> Типы.ОператорПрервать
			И Оператор.Тип <> Типы.ОператорПродолжить
			И Оператор.Тип <> Типы.ОператорПрисваивания
			И Оператор.Тип <> Типы.ОператорМетка
			И Оператор.Тип <> Типы.ОператорПерейти
			И Оператор.Тип <> Типы.ОператорВозврат
			И Оператор.Тип <> Типы.ОператорПока
			И Оператор.Тип <> Типы.ОператорДля
			И Оператор.Тип <> Типы.ОператорДляКаждого
			И Оператор.Тип <> Типы.ОператорВызватьИсключение Тогда
			Если Оператор.Тип = Типы.ОператорВызоваПроцедуры Тогда
				ВыдатьКоманду(Команды.LineNum, Оператор.Начало.НомерСтроки);
			Иначе
				ВыдатьКоманду(Команды.LineNum, Оператор.Конец.НомерСтроки);
			КонецЕсли;
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры // ПосетитьОператоры()

#Область ПосещениеОбъявлений

Процедура ПосетитьОбъявление(Объявление)
	Тип = Объявление.Тип;
	Если Тип = Типы.ОбъявлениеСпискаПеременныхМодуля Тогда
		Для Каждого ОбъявлениеПеременнойМодуля Из Объявление.Объявления Цикл
			ВыдатьПеременную(ОбъявлениеПеременнойМодуля.Имя, ОбъявлениеПеременнойМодуля);
		КонецЦикла;
	ИначеЕсли Тип = Типы.ОбъявлениеМетода Тогда
		ПосетитьОбъявлениеМетода(Объявление);
	ИначеЕсли Тип = Типы.ИнструкцияПрепроцессораОбласть
		Или Тип = Типы.ИнструкцияПрепроцессораКонецОбласти
		Или Тип = Типы.ИнструкцияПрепроцессораЕсли
		Или Тип = Типы.ИнструкцияПрепроцессораИначеЕсли
		Или Тип = Типы.ИнструкцияПрепроцессораИначе
		Или Тип = Типы.ИнструкцияПрепроцессораКонецЕсли Тогда
		ПосетитьИнструкциюПрепроцессора(Объявление);
	КонецЕсли;
КонецПроцедуры // ПосетитьОбъявление()

Процедура ПосетитьОбъявлениеМетода(ОбъявлениеМетода)
	ВыдатьМетод(ОбъявлениеМетода.Сигнатура.Имя, ОбъявлениеМетода.Сигнатура); // просто чтобы занять индекс
	ТочкиВходаМетодов.Вставить(ОбъявлениеМетода.Сигнатура.Имя, ИндексСледующейКоманды());
	Возвраты.Очистить();
	РазделПеременныхМетода = Новый Массив;
	ПеременныеМетодов.Вставить(ОбъявлениеМетода.Сигнатура.Имя, РазделПеременныхМетода);
	ЗначенияПараметровПоУмолчанию = Новый Массив;
	ЗначенияПараметровМетодовПоУмолчанию.Вставить(ОбъявлениеМетода.Сигнатура.Имя, ЗначенияПараметровПоУмолчанию);
	Для Каждого ОбъявлениеПараметра Из ОбъявлениеМетода.Сигнатура.Параметры Цикл
		ВыдатьПеременную(ОбъявлениеПараметра.Имя, ОбъявлениеПараметра);
		ЗначенияПараметровПоУмолчанию.Добавить(ОбъявлениеПараметра.Значение);
	КонецЦикла;
	Для Каждого ОбъявлениеЛокальнойПеременной Из ОбъявлениеМетода.Переменные Цикл
		ВыдатьПеременную(ОбъявлениеЛокальнойПеременной.Имя, ОбъявлениеЛокальнойПеременной);
	КонецЦикла;
	ПосетитьОператоры(ОбъявлениеМетода.Операторы);
	ИндексКонца = ВыдатьКоманду(Команды.LineNum, ОбъявлениеМетода.Конец.НомерСтроки);
	ВыдатьКоманду(Команды.End);
	Для Каждого Индекс Из Возвраты Цикл
		ВставитьКоманду(Индекс, Команды.Jmp, ИндексКонца);
	КонецЦикла;
	ВставитьПереходыНаМетки();
КонецПроцедуры // ПосетитьОбъявлениеМетода()

#КонецОбласти // ПосещениеОбъявлений

#Область ПосещениеВыражений

Процедура ПосетитьВыражение(Выражение)
	Тип = Выражение.Тип;
	Если Тип = Типы.ВыражениеЛитерал Тогда
		ПосетитьВыражениеЛитерал(Выражение);
	ИначеЕсли Тип = Типы.ВыражениеИдентификатор Тогда
		ПосетитьВыражениеИдентификатор(Выражение);
	ИначеЕсли Тип = Типы.ВыражениеУнарное Тогда
		ПосетитьВыражениеУнарное(Выражение);
	ИначеЕсли Тип = Типы.ВыражениеБинарное Тогда
		ПосетитьВыражениеБинарное(Выражение);
	ИначеЕсли Тип = Типы.ВыражениеНовый Тогда
		ПосетитьВыражениеНовый(Выражение);
	ИначеЕсли Тип = Типы.ВыражениеТернарное Тогда
		ПосетитьВыражениеТернарное(Выражение);
	ИначеЕсли Тип = Типы.ВыражениеСкобочное Тогда
		ПосетитьВыражениеСкобочное(Выражение);
	ИначеЕсли Тип = Типы.ВыражениеНе Тогда
		ПосетитьВыражениеНе(Выражение);
	ИначеЕсли Тип = Типы.ВыражениеСтроковое Тогда
		ПосетитьВыражениеСтроковое(Выражение);
	КонецЕсли;
КонецПроцедуры // ПосетитьВыражение()

Процедура ПосетитьВыражениеЛитерал(ВыражениеЛитерал)
	ВидЛитерала = ВыражениеЛитерал.Вид;
	Если ВидЛитерала = Токены.Неопределено Тогда
		ВыдатьКоманду(Команды.LdUndef);
	ИначеЕсли ВидЛитерала = Токены.Истина Тогда
		ВыдатьКоманду(Команды.LdTrue);
	ИначеЕсли ВидЛитерала = Токены.Ложь Тогда
		ВыдатьКоманду(Команды.LdFalse);
	ИначеЕсли ВидЛитерала = Токены.Null Тогда
		ВыдатьКоманду(Команды.LdNull);
	Иначе
		ВыдатьКоманду(Команды.LdConst, ВыдатьКонстанту(ВыражениеЛитерал.Значение));
	КонецЕсли;
КонецПроцедуры // ПосетитьВыражениеЛитерал()

Процедура ПосетитьВыражениеИдентификатор(ВыражениеИдентификатор, ВызовПроцедуры = Ложь)
	Если ВыражениеИдентификатор.Аргументы = Неопределено Тогда
		ВыдатьКомандуЗагрузкиПеременной(ВыражениеИдентификатор.Голова);
	Иначе
		РазделКомандНастоящий = РазделКоманд; // финт ушами чтобы индексы методов совпадали с платформой
		РазделКоманд = Новый Массив;
		КодФункции = Неопределено;
		Если Не ВызовПроцедуры Тогда
			ИмяНРег = НРег(ВыражениеИдентификатор.Голова.Имя);
			Если ИмяНРег = "min"
				Или ИмяНРег = "мин"
				Или ИмяНРег = "max"
				Или ИмяНРег = "макс" Тогда
				ВыдатьКоманду(Команды.ArgNum, ВыражениеИдентификатор.Аргументы.Количество());
				КодФункции = НайтиВстроеннуюФункцию(ВыражениеИдентификатор.Голова.Имя, Неопределено);
			Иначе
				КодФункции = НайтиВстроеннуюФункцию(ВыражениеИдентификатор.Голова.Имя, ВыражениеИдентификатор.Аргументы.Количество());
			КонецЕсли;
		КонецЕсли;
		Если КодФункции <> Неопределено Тогда
			ВыдатьКоманду(КодФункции);
		Иначе
			ВыдатьКоманду(Команды.ArgNum, ВыражениеИдентификатор.Аргументы.Количество());
			ВыдатьКоманду(Команды.CallLoc, ВыдатьМетод(ВыражениеИдентификатор.Голова.Имя, ВыражениеИдентификатор.Голова.Объявление, ВызовПроцедуры));
			Если Не ВызовПроцедуры Или ВыражениеИдентификатор.Хвост.Количество() > 0 Тогда
				ВыдатьКоманду(Команды.LdRet);
				ВыдатьКоманду(Команды.LineNum, ВыражениеИдентификатор.Начало.НомерСтроки);
			КонецЕсли;
		КонецЕсли;
		РазделКомандФиктивный = РазделКоманд;
		РазделКоманд = РазделКомандНастоящий;
		Для Каждого Выражение Из ВыражениеИдентификатор.Аргументы Цикл
			Если Выражение = Неопределено Тогда
				ВыдатьКоманду(Команды.LdNone);
			Иначе
				ПосетитьВыражение(Выражение);
			КонецЕсли;
		КонецЦикла;
		Для Каждого Команда Из РазделКомандФиктивный Цикл
			РазделКоманд.Добавить(Команда);
		КонецЦикла;
	КонецЕсли;
	ПосетитьХвост(ВыражениеИдентификатор.Хвост, ВызовПроцедуры);
КонецПроцедуры // ПосетитьВыражениеИдентификатор()

Процедура ПосетитьХвост(Хвост, ВызовПроцедуры = Ложь)

	Для Каждого Элемент Из Хвост Цикл
		Если Элемент.Тип = Типы.ВыражениеПоле Тогда
			Если Элемент.Аргументы = Неопределено Тогда
				ВыдатьКоманду(Команды.LdProp, ВыдатьКонстанту(Элемент.Имя));
			Иначе
				ИндексИмениМетода = ВыдатьКонстанту(Элемент.Имя); // до выражения чтобы индекс с платформой совпадал
				Для Каждого Выражение Из Элемент.Аргументы Цикл
					Если Выражение = Неопределено Тогда
						ВыдатьКоманду(Команды.LdNone);
					Иначе
						ПосетитьВыражение(Выражение);
					КонецЕсли;
				КонецЦикла;
				ВыдатьКоманду(Команды.ArgNum, Элемент.Аргументы.Количество());
				Если ВызовПроцедуры Тогда
					ВыдатьКоманду(Команды.CallProc, ИндексИмениМетода);
				Иначе
					ВыдатьКоманду(Команды.CallFunc, ИндексИмениМетода);
					ВыдатьКоманду(Команды.LdRet);
					ВыдатьКоманду(Команды.LineNum, Элемент.Начало.НомерСтроки);
				КонецЕсли;
			КонецЕсли;
		ИначеЕсли Элемент.Тип = Типы.ВыражениеИндекс Тогда
			ПосетитьВыражение(Элемент.Выражение);
			ВыдатьКоманду(Команды.LdIndex);
		Иначе
			ВызватьИсключение "нарушение протокола";
		КонецЕсли;
	КонецЦикла;

КонецПроцедуры

Процедура ПосетитьВыражениеУнарное(ВыражениеУнарное)
	ПосетитьВыражение(ВыражениеУнарное.Операнд);
	Если ВыражениеУнарное.Операция.Токен = Токены.ЗнакВычитания Тогда
		ВыдатьКоманду(Команды.Minus);
	КонецЕсли;
КонецПроцедуры // ПосетитьВыражениеУнарное()

Процедура ПосетитьВыражениеБинарное(ВыражениеБинарное)
	ЭтоЛогическаяОперация = (
		ВыражениеБинарное.Операция.Токен = Токены.И
		Или ВыражениеБинарное.Операция.Токен = Токены.Или
	);
	ПосетитьВыражение(ВыражениеБинарное.ЛевыйОперанд);
	Если ЭтоЛогическаяОперация Тогда
		ИндексПерехода = ВыдатьЗаглушку();
	КонецЕсли;
	ПосетитьВыражение(ВыражениеБинарное.ПравыйОперанд);
	Если ЭтоЛогическаяОперация Тогда
		ВыдатьКоманду(Команды.Boolean);
		ВставитьКоманду(ИндексПерехода, КомандыБинарныхОпераций[ВыражениеБинарное.Операция.Токен], ИндексСледующейКоманды());
	Иначе
		ВыдатьКоманду(КомандыБинарныхОпераций[ВыражениеБинарное.Операция.Токен]);
	КонецЕсли;
КонецПроцедуры // ПосетитьВыражениеБинарное()

Процедура ПосетитьВыражениеНовый(ВыражениеНовый)
	Для Каждого Выражение Из ВыражениеНовый.Аргументы Цикл
		Если Выражение <> Неопределено Тогда
			ПосетитьВыражение(Выражение);
		КонецЕсли;
	КонецЦикла;
	Если ВыражениеНовый.Имя <> Неопределено Тогда
		ВыдатьКоманду(Команды.ArgNum, ВыражениеНовый.Аргументы.Количество());
		ВыдатьКоманду(Команды.New, ВыдатьКонстанту(ВыражениеНовый.Имя));
	Иначе
		КодФункции = НайтиВстроеннуюФункцию("New", ВыражениеНовый.Аргументы.Количество());
		Если КодФункции <> Неопределено Тогда
			ВыдатьКоманду(КодФункции);
		Иначе
			ВызватьИсключение "ошибка компиляции";
		КонецЕсли;
	КонецЕсли;
КонецПроцедуры // ПосетитьВыражениеНовый()

Процедура ПосетитьВыражениеТернарное(ВыражениеТернарное)
	ПосетитьВыражение(ВыражениеТернарное.Выражение);
	ИндексУсловия = ВыдатьЗаглушку();
	ПосетитьВыражение(ВыражениеТернарное.Тогда);
	ИндексПереходаНаКонец = ВыдатьЗаглушку();
	ВставитьКоманду(ИндексУсловия, Команды.JmpFalse, ИндексСледующейКоманды());
	ПосетитьВыражение(ВыражениеТернарное.Иначе);
	ВставитьКоманду(ИндексПереходаНаКонец, Команды.Jmp, ИндексСледующейКоманды());
	ПосетитьХвост(ВыражениеТернарное.Хвост);
КонецПроцедуры // ПосетитьВыражениеТернарное()

Процедура ПосетитьВыражениеСкобочное(ВыражениеСкобочное)
	ПосетитьВыражение(ВыражениеСкобочное.Выражение);
КонецПроцедуры // ПосетитьВыражениеСкобочное()

Процедура ПосетитьВыражениеНе(ВыражениеНе)
	ПосетитьВыражение(ВыражениеНе.Выражение);
	ВыдатьКоманду(Команды.Not);
КонецПроцедуры // ПосетитьВыражениеНе()

Процедура ПосетитьВыражениеСтроковое(ВыражениеСтроковое)
	СписокЧастей = Новый Массив;
	Для Каждого Выражение Из ВыражениеСтроковое.Элементы Цикл
		СписокЧастей.Добавить(Выражение.Значение);
	КонецЦикла;
	ВыдатьКоманду(Команды.LdConst, ВыдатьКонстанту(СтрСоединить(СписокЧастей, Символы.ПС)));
КонецПроцедуры // ПосетитьВыражениеСтроковое()

#КонецОбласти // ПосещениеВыражений

#Область ПосещениеОператоров

Процедура ПосетитьОператор(Оператор)
	Тип = Оператор.Тип;
	Если Тип = Типы.ОператорПрисваивания Тогда
		ПосетитьОператорПрисваивания(Оператор);
	ИначеЕсли Тип = Типы.ОператорВозврат Тогда
		ПосетитьОператорВозврат(Оператор);
	ИначеЕсли Тип = Типы.ОператорПрервать Тогда
		ПосетитьОператорПрервать(Оператор);
	ИначеЕсли Тип = Типы.ОператорПродолжить Тогда
		ПосетитьОператорПродолжить(Оператор);
	ИначеЕсли Тип = Типы.ОператорВызватьИсключение Тогда
		ПосетитьОператорВызватьИсключение(Оператор);
	ИначеЕсли Тип = Типы.ОператорВыполнить Тогда
		ПосетитьОператорВыполнить(Оператор);
	ИначеЕсли Тип = Типы.ОператорВызоваПроцедуры Тогда
		ПосетитьОператорВызоваПроцедуры(Оператор);
	ИначеЕсли Тип = Типы.ОператорЕсли Тогда
		ПосетитьОператорЕсли(Оператор);
	ИначеЕсли Тип = Типы.ОператорПока Тогда
		ПосетитьОператорПока(Оператор);
	ИначеЕсли Тип = Типы.ОператорДля Тогда
		ПосетитьОператорДля(Оператор);
	ИначеЕсли Тип = Типы.ОператорДляКаждого Тогда
		ПосетитьОператорДляКаждого(Оператор);
	ИначеЕсли Тип = Типы.ОператорПопытка Тогда
		ПосетитьОператорПопытка(Оператор);
	ИначеЕсли Тип = Типы.ОператорПерейти Тогда
		ПосетитьОператорПерейти(Оператор);
	ИначеЕсли Тип = Типы.ОператорМетка Тогда
		ПосетитьОператорМетка(Оператор);
	ИначеЕсли Тип = Типы.ИнструкцияПрепроцессораОбласть
		Или Тип = Типы.ИнструкцияПрепроцессораКонецОбласти
		Или Тип = Типы.ИнструкцияПрепроцессораЕсли
		Или Тип = Типы.ИнструкцияПрепроцессораИначеЕсли
		Или Тип = Типы.ИнструкцияПрепроцессораИначе
		Или Тип = Типы.ИнструкцияПрепроцессораКонецЕсли Тогда
		ПосетитьИнструкциюПрепроцессора(Оператор);
	КонецЕсли;
КонецПроцедуры // ПосетитьОператор()

Процедура ПосетитьОператорПрисваивания(ОператорПрисваивания)
	ПосетитьВыражениеИдентификатор(ОператорПрисваивания.ЛевыйОперанд);
	ПосетитьВыражение(ОператорПрисваивания.ПравыйОперанд);
	ВыдатьКоманду(Команды.Assign);
КонецПроцедуры // ПосетитьОператорПрисваивания()

Процедура ПосетитьОператорВозврат(ОператорВозврат)
	Если ОператорВозврат.Выражение <> Неопределено Тогда
		ПосетитьВыражение(ОператорВозврат.Выражение);
		ВыдатьКоманду(Команды.Ret);
	КонецЕсли;
	ВыдатьКоманду(Команды.BlckEnd);
	Возвраты.Добавить(ВыдатьЗаглушку());
КонецПроцедуры // ПосетитьОператорВозврат()

Процедура ПосетитьОператорПрервать(ОператорПрервать)
	ВзятьПоследнееЗначение(СтекПереходовВЦиклах).Прервать.Добавить(ВыдатьЗаглушку());
КонецПроцедуры // ПосетитьОператорПрервать()

Процедура ПосетитьОператорПродолжить(ОператорПродолжить)
	ВзятьПоследнееЗначение(СтекПереходовВЦиклах).Продолжить.Добавить(ВыдатьЗаглушку());
КонецПроцедуры // ПосетитьОператорПродолжить()

Процедура ПосетитьОператорВызватьИсключение(ОператорВызватьИсключение)
	Если ОператорВызватьИсключение.Выражение <> Неопределено Тогда
		ПосетитьВыражение(ОператорВызватьИсключение.Выражение);
		ВыдатьКоманду(Команды.Raise, 1);
	Иначе
		ВыдатьКоманду(Команды.Raise, 0);
	КонецЕсли;
КонецПроцедуры // ПосетитьОператорВызватьИсключение()

Процедура ПосетитьОператорВыполнить(ОператорВыполнить)
	ПосетитьВыражение(ОператорВыполнить.Выражение);
	ВыдатьКоманду(Команды.Exec);
КонецПроцедуры // ПосетитьОператорВыполнить()

Процедура ПосетитьОператорВызоваПроцедуры(ОператорВызоваПроцедуры)
	ПосетитьВыражениеИдентификатор(ОператорВызоваПроцедуры.Идентификатор, Истина);
КонецПроцедуры // ПосетитьОператорВызоваПроцедуры()

Процедура ПосетитьОператорЕсли(ОператорЕсли)
	ПосетитьВыражение(ОператорЕсли.Выражение);
	ИндексПоследнегоУсловия = ВыдатьЗаглушку();
	ИндексыПереходовНаИначеИлиКонец = Новый Массив;
	ПосетитьОператоры(ОператорЕсли.Тогда);
	Если ОператорЕсли.ИначеЕсли <> Неопределено Тогда
		Для Каждого ОператорИначеЕсли Из ОператорЕсли.ИначеЕсли Цикл
			ИндексыПереходовНаИначеИлиКонец.Добавить(ВыдатьЗаглушку());
			ВыдатьКоманду(Команды.LineNum, ОператорИначеЕсли.Начало.НомерСтроки);
			ВставитьКоманду(ИндексПоследнегоУсловия, Команды.JmpFalse, ИндексТекущейКоманды());
			ПосетитьВыражение(ОператорИначеЕсли.Выражение);
			ИндексПоследнегоУсловия = ВыдатьЗаглушку();
			ПосетитьОператоры(ОператорИначеЕсли.Тогда);
		КонецЦикла;
	КонецЕсли;
	Если ОператорЕсли.Иначе <> Неопределено Тогда
		ИндексыПереходовНаИначеИлиКонец.Добавить(ВыдатьЗаглушку());
		ВставитьКоманду(ИндексПоследнегоУсловия, Команды.JmpFalse, ИндексСледующейКоманды());
		ПосетитьОператоры(ОператорЕсли.Иначе.Операторы);
	Иначе
		ИндексыПереходовНаИначеИлиКонец.Добавить(ВыдатьЗаглушку());
		ВставитьКоманду(ИндексПоследнегоУсловия, Команды.JmpFalse, ИндексСледующейКоманды());
	КонецЕсли;
	ИндексСледующейКоманды = ИндексСледующейКоманды();
	Для Каждого Индекс Из ИндексыПереходовНаИначеИлиКонец Цикл
		ВставитьКоманду(Индекс, Команды.Jmp, ИндексСледующейКоманды);
	КонецЦикла;
КонецПроцедуры // ПосетитьОператорЕсли()

Процедура ПосетитьОператорПока(ОператорПока)
	ПередПосещениемЦикла();
	ИндексНачала = РазделКоманд.ВГраница();
	ПосетитьВыражение(ОператорПока.Выражение);
	ВыдатьКоманду(Команды.LineNum, ОператорПока.Начало.НомерСтроки);
	ИндексУсловия = ВыдатьЗаглушку();
	ПосетитьОператоры(ОператорПока.Операторы);
	ВыдатьКоманду(Команды.LineNum, ОператорПока.Конец.НомерСтроки);
	ВыдатьКоманду(Команды.Jmp, ИндексНачала);
	ИндексКонца = ИндексСледующейКоманды();
	ВставитьКоманду(ИндексУсловия, Команды.JmpFalse, ИндексКонца);
	ПослеПосещенияЦикла(ИндексНачала, ИндексКонца);
КонецПроцедуры // ПосетитьОператорПока()

Процедура ПосетитьОператорДля(ОператорДля)
	ПередПосещениемЦикла();
	ПосетитьВыражениеИдентификатор(ОператорДля.Идентификатор);
	ПосетитьВыражение(ОператорДля.Старт);
	ВыдатьКоманду(Команды.Assign);
	ВыдатьКоманду(Команды.PutTmp);
	ВыдатьКоманду(Команды.LdTmp);
	ПосетитьВыражение(ОператорДля.Финиш);
	ВыдатьКоманду(Команды.Assign);
	ИндексНачала = ВыдатьКоманду(Команды.LineNum, ОператорДля.Начало.НомерСтроки);
	ВыдатьКоманду(Команды.LdTmp);
	ПосетитьВыражениеИдентификатор(ОператорДля.Идентификатор);
	ВыдатьКоманду(Команды.Gte);
	ИндексУсловия = ВыдатьЗаглушку();
	ПосетитьОператоры(ОператорДля.Операторы);
	ВыдатьКоманду(Команды.LineNum, ОператорДля.Конец.НомерСтроки);
	ИндексПродолжения = ИндексСледующейКоманды();
	ПосетитьВыражениеИдентификатор(ОператорДля.Идентификатор);
	ВыдатьКоманду(Команды.Inc);
	ВыдатьКоманду(Команды.Jmp, ИндексНачала);
	ИндексКонца = ВыдатьКоманду(Команды.PopTmp, 1);
	ВставитьКоманду(ИндексУсловия, Команды.JmpFalse, ИндексКонца);
	ПослеПосещенияЦикла(ИндексПродолжения, ИндексКонца);
КонецПроцедуры // ПосетитьОператорДля()

Процедура ПосетитьОператорДляКаждого(ОператорДляКаждого)
	ПередПосещениемЦикла();
	ВыдатьПеременную(ОператорДляКаждого.Идентификатор.Голова.Имя, ОператорДляКаждого.Идентификатор.Голова.Объявление); // просто чтоб индекс занять
	ВременнаяПеременная = ВременнаяПеременная(ОператорДляКаждого.Идентификатор.Голова);
	ВыдатьПеременную(ВременнаяПеременная.Имя, ВременнаяПеременная.Объявление); // просто чтоб индекс занять
	ВыдатьКоманду(Команды.PutTmp);
	ВыдатьКоманду(Команды.LdTmp);
	ПосетитьВыражение(ОператорДляКаждого.Коллекция);
	ВыдатьКоманду(Команды.Iter);
	ВыдатьКоманду(Команды.Assign);
	ВыдатьКоманду(Команды.LineNum, ОператорДляКаждого.Начало.НомерСтроки);
	ИндексНачала = ИндексТекущейКоманды();
	ВыдатьКомандуЗагрузкиПеременной(ВременнаяПеременная);
	ВыдатьКоманду(Команды.LdTmp);
	ВыдатьКоманду(Команды.Assign);
	ВыдатьКомандуЗагрузкиПеременной(ВременнаяПеременная);
	ВыдатьКоманду(Команды.Next);
	ИндексУсловия = ВыдатьЗаглушку();
	ПосетитьВыражениеИдентификатор(ОператорДляКаждого.Идентификатор);
	ВыдатьКомандуЗагрузкиПеременной(ВременнаяПеременная);
	ВыдатьКоманду(Команды.Assign);
	ВыдатьКомандуЗагрузкиПеременной(ВременнаяПеременная);
	ВыдатьКоманду(Команды.LdUndef);
	ВыдатьКоманду(Команды.Assign);
	ПосетитьОператоры(ОператорДляКаждого.Операторы);
	ВыдатьКоманду(Команды.LineNum, ОператорДляКаждого.Конец.НомерСтроки);
	ИндексПродолжения = ВыдатьКоманду(Команды.Jmp, ИндексНачала);
	ИндексКонца = ВыдатьКоманду(Команды.PopTmp, 1);
	ВставитьКоманду(ИндексУсловия, Команды.JmpFalse, ИндексКонца);
	ВыдатьКомандуЗагрузкиПеременной(ВременнаяПеременная);
	ВыдатьКоманду(Команды.LdUndef);
	ВыдатьКоманду(Команды.Assign);
	// хз зачем второй раз, но так делает платформа
	ВыдатьКомандуЗагрузкиПеременной(ВременнаяПеременная);
	ВыдатьКоманду(Команды.LdUndef);
	ВыдатьКоманду(Команды.Assign);
	ПослеПосещенияЦикла(ИндексПродолжения, ИндексКонца);
КонецПроцедуры // ПосетитьОператорДляКаждого()

Процедура ПосетитьОператорПопытка(ОператорПопытка)
	ИндексНачала = ВыдатьЗаглушку();
	ПосетитьОператоры(ОператорПопытка.Попытка);
	ВыдатьКоманду(Команды.LineNum, ОператорПопытка.Конец.НомерСтроки);
	ВыдатьКоманду(Команды.BlckEnd, 1);
	ИндексПерехода = ВыдатьЗаглушку();
	ВставитьКоманду(ИндексНачала, Команды.BeginTry, ИндексСледующейКоманды());
	ПосетитьОператоры(ОператорПопытка.Исключение.Операторы);
	ВыдатьКоманду(Команды.LineNum, ОператорПопытка.Конец.НомерСтроки);
	ВыдатьКоманду(Команды.EndTry);
	ВставитьКоманду(ИндексПерехода, Команды.Jmp, ИндексСледующейКоманды());
КонецПроцедуры // ПосетитьОператорПопытка()

Процедура ПосетитьОператорПерейти(ОператорПерейти)
	СписокПереходовНаМетку = СписокПереходовНаМетку(ОператорПерейти.Метка);
	СписокПереходовНаМетку.Добавить(ВыдатьЗаглушку());
КонецПроцедуры // ПосетитьОператорПерейти()

Процедура ПосетитьОператорМетка(ОператорМетка)
	Метки[НРег(ОператорМетка.Метка)] = Новый Структура("ИндексМетки, ИндексКоманды", Метки.Количество(), ИндексСледующейКоманды());
КонецПроцедуры // ПосетитьОператорМетка()

#КонецОбласти // ПосещениеОператоров

#Область ПосещениеВыраженийПрепроцессора

// полный игнор

Процедура ПосетитьВыражениеПрепроцессора(ВыражениеПрепроцессора)
	Тип = ВыражениеПрепроцессора.Тип;
	Если Тип = Типы.ВыражениеПрепроцессораСимвол Тогда
		ПосетитьВыражениеПрепроцессораСимвол(ВыражениеПрепроцессора);
	ИначеЕсли Тип = Типы.ВыражениеПрепроцессораБинарное Тогда
		ПосетитьВыражениеПрепроцессораБинарное(ВыражениеПрепроцессора);
	ИначеЕсли Тип = Типы.ВыражениеПрепроцессораНе Тогда
		ПосетитьВыражениеПрепроцессораНе(ВыражениеПрепроцессора);
	ИначеЕсли Тип = Типы.ВыражениеПрепроцессораСкобочное Тогда
		ПосетитьВыражениеПрепроцессораСкобочное(ВыражениеПрепроцессора);
	КонецЕсли;
КонецПроцедуры // ПосетитьВыражениеПрепроцессора()

Процедура ПосетитьВыражениеПрепроцессораСимвол(ВыражениеПрепроцессораСимвол)

КонецПроцедуры // ПосетитьВыражениеПрепроцессораСимвол()

Процедура ПосетитьВыражениеПрепроцессораБинарное(ВыражениеПрепроцессораБинарное)
	ПосетитьВыражениеПрепроцессора(ВыражениеПрепроцессораБинарное.ЛевыйОперанд);
	ПосетитьВыражениеПрепроцессора(ВыражениеПрепроцессораБинарное.ПравыйОперанд);
КонецПроцедуры // ПосетитьВыражениеПрепроцессораБинарное()

Процедура ПосетитьВыражениеПрепроцессораНе(ВыражениеПрепроцессораНе)
	ПосетитьВыражениеПрепроцессора(ВыражениеПрепроцессораНе.Выражение);
КонецПроцедуры // ПосетитьВыражениеПрепроцессораНе()

Процедура ПосетитьВыражениеПрепроцессораСкобочное(ВыражениеПрепроцессораСкобочное)
	ПосетитьВыражениеПрепроцессора(ВыражениеПрепроцессораСкобочное.Выражение);
КонецПроцедуры // ПосетитьВыражениеПрепроцессораСкобочное()

Процедура ПосетитьИнструкциюПрепроцессора(ИнструкцияПрепроцессора)
	Если ИнструкцияПрепроцессора.Владелец().Колонки.Найти("Выражение") <> Неопределено Тогда
		ПосетитьВыражениеПрепроцессора(ИнструкцияПрепроцессора.Выражение);
	КонецЕсли;
КонецПроцедуры // ПосетитьИнструкциюПрепроцессора()

#КонецОбласти // ПосещениеВыраженийПрепроцессора

#Область СлужебныеМетоды

Функция ВыдатьКоманду(Команда, Параметр = 0)

	ОписаниеКоманды = Новый Массив;
	ОписаниеКоманды.Добавить(Команда);
	ОписаниеКоманды.Добавить(Параметр);
	РазделКоманд.Добавить(ОписаниеКоманды);

	Возврат РазделКоманд.ВГраница();

КонецФункции

Функция ВыдатьЗаглушку()

	РазделКоманд.Добавить(Неопределено);

	Возврат РазделКоманд.ВГраница();

КонецФункции

Процедура ВставитьКоманду(Индекс, Команда, Параметр = 0)

	ОписаниеКоманды = Новый Массив;
	ОписаниеКоманды.Добавить(Команда);
	ОписаниеКоманды.Добавить(Параметр);
	РазделКоманд[Индекс] = ОписаниеКоманды;

КонецПроцедуры

Функция ИндексТекущейКоманды()
	Возврат РазделКоманд.ВГраница();
КонецФункции

Функция ИндексСледующейКоманды()
	Возврат РазделКоманд.Количество();
КонецФункции

Функция ВыдатьКонстанту(Значение)

	Индекс = КэшКонстант[Значение];

	Если Индекс = Неопределено Тогда

		РазделКонстант.Добавить(Значение);
		Индекс = РазделКонстант.ВГраница();

		КэшКонстант[Значение] = Индекс;

	КонецЕсли;

	Возврат Индекс;

КонецФункции

Функция ВыдатьПеременную(Имя, Знач Объявление)

	Если Объявление = Неопределено Тогда
		Объявление = ФиктивныеОбъявления[НРег(Имя)];
	КонецЕсли;

	Индекс = КэшПеременных[Объявление];

	Если Индекс = Неопределено Тогда

		Если Объявление = Неопределено Тогда
			Объявление = Новый Структура("Тип", "ФиктивноеОбъявление");
			ФиктивныеОбъявления[НРег(Имя)] = Объявление;
		КонецЕсли;

		Если ЭтоТелоМодуля
			Или Объявление.Тип = Типы.ОбъявлениеПеременнойМодуля
			Или Объявление.Тип = Типы.ОбъявлениеГлобальногоОбъекта
			Или Объявление.Тип = "ФиктивноеОбъявление" Тогда
			Раздел = РазделПеременных;
			КомандыЗагрузкиПеременных[Объявление] = Команды.LdVar;
		Иначе
			Раздел = РазделПеременныхМетода;
			КомандыЗагрузкиПеременных[Объявление] = Команды.LdLoc;
		КонецЕсли;

		Флаги = 0;

		Если Объявление.Тип = Типы.ОбъявлениеГлобальногоОбъекта
			Или Объявление.Тип = "ФиктивноеОбъявление" Тогда
			Флаги = Флаги + ФлагиПеременных.Глобальная;
		Иначе
			Если Объявление.Тип = Типы.ОбъявлениеПеременнойМодуля Тогда
				Если Объявление.Экспорт Тогда
					Флаги = Флаги + ФлагиПеременных.Экспорт;
				КонецЕсли;
			Иначе
				Флаги = Флаги + ФлагиПеременных.Локальная;
			КонецЕсли;
		КонецЕсли;

		Если Объявление.Тип = Типы.ОбъявлениеПараметра Тогда
			Флаги = Флаги + ФлагиПеременных.Параметр;
			Если Объявление.ПоЗначению Тогда
				Флаги = Флаги + ФлагиПеременных.Знач;
			КонецЕсли;
		КонецЕсли;

		ОписаниеПеременной = Новый Массив;
		ОписаниеПеременной.Добавить(Имя);
		ОписаниеПеременной.Добавить(Флаги);
		ОписаниеПеременной.Добавить(-1);

		Раздел.Добавить(ОписаниеПеременной);
		Индекс = Раздел.ВГраница();

		КэшПеременных[Объявление] = Индекс;

	КонецЕсли;

	Возврат Индекс;

КонецФункции

Функция ВыдатьМетод(Имя, Сигнатура, ВызовПроцедуры = Истина)

	Индекс = КэшМетодов[НРег(Имя)];

	Если Индекс = Неопределено Тогда

		Флаги = 0;

		Если Сигнатура = Неопределено Или Сигнатура.Тип = Типы.ОбъявлениеГлобальногоМетода Тогда
			Флаги = Флаги + ФлагиМетодов.Глобальный;
			Если Не ВызовПроцедуры Тогда
				Флаги = Флаги + ФлагиМетодов.Функция;
			КонецЕсли;
		Иначе
			Если Сигнатура.Тип = Типы.ОбъявлениеСигнатурыФункции Тогда
				Флаги = Флаги + ФлагиМетодов.Функция;
			КонецЕсли;
			Если Сигнатура.Экспорт Тогда
				Флаги = Флаги + ФлагиМетодов.Экспорт;
			КонецЕсли;
		КонецЕсли;

		ОписаниеМетода = Новый Массив;
		ОписаниеМетода.Добавить(Имя);
		ОписаниеМетода.Добавить(Флаги);
		Если Сигнатура = Неопределено Тогда
			ОписаниеМетода.Добавить(0); // число параметров неизвестно
		Иначе
			ОписаниеМетода.Добавить(Сигнатура.Параметры.Количество());
		КонецЕсли;
		ОписаниеМетода.Добавить(0); // точка входа (см. ТочкиВходаМетодов)

		РазделМетодов.Добавить(ОписаниеМетода);
		Индекс = РазделМетодов.ВГраница();

		КэшМетодов[НРег(Имя)] = Индекс;

	КонецЕсли;

	Возврат Индекс;

КонецФункции

Функция СписокПереходовНаМетку(Имя)

	Список = ПереходыНаМетки[НРег(Имя)];

	Если Список = Неопределено Тогда
		Список = Новый Массив;
		ПереходыНаМетки[НРег(Имя)] = Список;
	КонецЕсли;

	Возврат Список;

КонецФункции

Функция ВременнаяПеременная(ЭлементОкружения)
	ВременнаяПеременная = КэшВременныхПеременных[ЭлементОкружения];
	ФиктивноеОбъявление = ЭлементОкружения.Объявление.Владелец().Добавить();
	ЗаполнитьЗначенияСвойств(ФиктивноеОбъявление, ЭлементОкружения.Объявление);
	Если ВременнаяПеременная = Неопределено Тогда
		ВременнаяПеременная = Новый Структура(
			"Тип,"        // строка (один из Типы)
			"Имя,"        // строка
			"Объявление", // неопределено, структура (один из #Объявления)
			Типы.ЭлементОкружения, "0" + ЭлементОкружения.Имя, ФиктивноеОбъявление);
		КэшВременныхПеременных[ЭлементОкружения] = ВременнаяПеременная;
	КонецЕсли;
	Возврат ВременнаяПеременная;
КонецФункции

Процедура ВыдатьКомандуЗагрузкиПеременной(ЭлементОкружения)
	ИндексПеременной = ВыдатьПеременную(ЭлементОкружения.Имя, ЭлементОкружения.Объявление);
	Объявление = ЭлементОкружения.Объявление;
	Если Объявление = Неопределено Тогда
		Объявление = ФиктивныеОбъявления[НРег(ЭлементОкружения.Имя)];
	КонецЕсли;
	КомандаЗагрузки = КомандыЗагрузкиПеременных[Объявление];
	ВыдатьКоманду(КомандаЗагрузки, ИндексПеременной);
КонецПроцедуры

Функция НайтиВстроеннуюФункцию(Имя, КоличествоПараметров)

	Фильтр = Новый Структура;
	Фильтр.Вставить("Имя", НРег(Имя));
	Фильтр.Вставить("КоличествоПараметров", КоличествоПараметров);
	ИскомыеСтроки = ВстроенныеФункции.НайтиСтроки(Фильтр);
	Если ИскомыеСтроки.Количество() > 0 Тогда
		Возврат ИскомыеСтроки[0].Код;
	КонецЕсли;

	Возврат Неопределено;

КонецФункции

Процедура ПередПосещениемЦикла()

	СтекПереходовВЦиклах.Добавить(Новый Структура("Прервать, Продолжить", Новый Массив, Новый Массив));

КонецПроцедуры

Процедура ПослеПосещенияЦикла(ИндексНачала, ИндексКонца)

	Переходы = СнятьПоследнееЗначение(СтекПереходовВЦиклах);

	Для Каждого ИндексПерехода Из Переходы.Продолжить Цикл
		ВставитьКоманду(ИндексПерехода, Команды.Jmp, ИндексНачала);
	КонецЦикла;

	Для Каждого ИндексПерехода Из Переходы.Прервать Цикл
		ВставитьКоманду(ИндексПерехода, Команды.Jmp, ИндексКонца);
	КонецЦикла;

КонецПроцедуры

Функция СнятьПоследнееЗначение(Стек)

	ИндексВершиныСтека = Стек.ВГраница();
	Значение = Стек[ИндексВершиныСтека];
	Стек.Удалить(ИндексВершиныСтека);

	Возврат Значение;

КонецФункции

Функция ВзятьПоследнееЗначение(Стек)

	Возврат Стек[Стек.ВГраница()];

КонецФункции

Процедура ВставитьПереходыНаМетки()
	Для Каждого ПереходыНаМетку Из ПереходыНаМетки Цикл
		ИндексМетки = Метки[ПереходыНаМетку.Ключ].ИндексМетки;
		Для Каждого ИндексПерехода Из ПереходыНаМетку.Значение Цикл
			ВставитьКоманду(ИндексПерехода, Команды.Goto, ИндексМетки);
		КонецЦикла;
	КонецЦикла;
	ПереходыНаМетки.Очистить();
	Метки.Очистить();
КонецПроцедуры

Процедура ДобавитьВстроеннуюФункцию(ИмяНаРусском, ИмяНаАнглийском, КоличествоПараметров, Код)

	НоваяСтрока = ВстроенныеФункции.Добавить();
	НоваяСтрока.Имя = Нрег(ИмяНаРусском);
	НоваяСтрока.КоличествоПараметров = КоличествоПараметров;
	НоваяСтрока.Код = Код;

	НоваяСтрока = ВстроенныеФункции.Добавить();
	НоваяСтрока.Имя = Нрег(ИмяНаАнглийском);
	НоваяСтрока.КоличествоПараметров = КоличествоПараметров;
	НоваяСтрока.Код = Код;

КонецПроцедуры

#КонецОбласти // СлужебныеМетоды

РазделКоманд = Новый Массив;
РазделКонстант = Новый Массив;
РазделПеременных = Новый Массив;
РазделМетодов = Новый Массив;
РазделПеременныхМетода = Новый Массив;

ПеременныеМетодов = Новый Структура;
ЗначенияПараметровМетодовПоУмолчанию = Новый Структура;

КомандыЗагрузкиПеременных = Новый Соответствие;
КомандыБинарныхОпераций = Новый Соответствие;

КэшКонстант = Новый Соответствие;
КэшПеременных = Новый Соответствие;
КэшМетодов = Новый Соответствие;
КэшВременныхПеременных = Новый Соответствие;
Возвраты = Новый Массив;
Метки = Новый Соответствие;
ПереходыНаМетки = Новый Соответствие;
ПереходыНаКонецМодуля = Новый Массив;

ФиктивныеОбъявления = Новый Соответствие;

СтекПереходовВЦиклах = Новый Массив;

ЭтоТелоМодуля = Ложь;

ФлагиПеременных = Новый Структура;
ФлагиПеременных.Вставить("Локальная", 1);
ФлагиПеременных.Вставить("Глобальная", 2);
ФлагиПеременных.Вставить("Параметр", 4);
ФлагиПеременных.Вставить("Знач", 8);
ФлагиПеременных.Вставить("Экспорт", 16);

ФлагиМетодов = Новый Структура;
ФлагиМетодов.Вставить("Функция", 1);
ФлагиМетодов.Вставить("Экспорт", 16);
ФлагиМетодов.Вставить("Глобальный", 32);

ТочкаВходаМодуля = 0;
ТочкиВходаМетодов = Новый Структура;

Команды = Новый Структура;
Команды.Вставить("Nop", 0);
Команды.Вставить("LineNum", 1);
Команды.Вставить("LdVar", 2);
Команды.Вставить("LdLoc", 3);
Команды.Вставить("LdConst", 4);
Команды.Вставить("LdRet", 5);
Команды.Вставить("LdFalse", 6);
Команды.Вставить("LdTrue", 7);
Команды.Вставить("LdUndef", 8);
Команды.Вставить("LdNull", 9);
Команды.Вставить("LdNone", 10);
Команды.Вставить("Unknown", 11);
Команды.Вставить("LdProp", 12);
Команды.Вставить("LdIndex", 13);
Команды.Вставить("Iter", 14);
Команды.Вставить("Next", 15);
Команды.Вставить("Assign", 16);
Команды.Вставить("Ret", 17);
Команды.Вставить("ArgNum", 18);
Команды.Вставить("CallLoc", 19);
Команды.Вставить("CallProc", 20);
Команды.Вставить("CallFunc", 21);
Команды.Вставить("End", 22);
Команды.Вставить("Minus", 23);
Команды.Вставить("Add", 24);
Команды.Вставить("Sub", 25);
Команды.Вставить("Mul", 26);
Команды.Вставить("Div", 27);
Команды.Вставить("Mod", 28);
Команды.Вставить("Not", 29);
Команды.Вставить("And", 30);
Команды.Вставить("Or", 31);
Команды.Вставить("Unused", 32);
Команды.Вставить("Cmp", 33);
Команды.Вставить("Ne", 34);
Команды.Вставить("Gt", 35);
Команды.Вставить("Lt", 36);
Команды.Вставить("Gte", 37);
Команды.Вставить("Lte", 38);
Команды.Вставить("Jmp", 39);
Команды.Вставить("JmpFalse", 40);
Команды.Вставить("JmpTrue", 41);
Команды.Вставить("Goto", 42);
Команды.Вставить("Inc", 43);
Команды.Вставить("BeginTry", 44);
Команды.Вставить("BlckEnd", 45);
Команды.Вставить("EndTry", 46);
Команды.Вставить("Raise", 47);
Команды.Вставить("PutTmp", 48);
Команды.Вставить("LdTmp", 49);
Команды.Вставить("PopTmp", 50);
Команды.Вставить("New", 51);
Команды.Вставить("Exec", 52);
Команды.Вставить("Boolean", 69); // так же есть во встроенных функциях
Команды.Вставить("ДобавитьОбработчикЛокальный", 123);
Команды.Вставить("ДобавитьОбработчик", 124);
Команды.Вставить("УдалитьОбработчикЛокальный", 125);
Команды.Вставить("УдалитьОбработчик", 126);

ВстроенныеФункции = Новый ТаблицаЗначений;
ВстроенныеФункции.Колонки.Добавить("Имя");
ВстроенныеФункции.Колонки.Добавить("КоличествоПараметров");
ВстроенныеФункции.Колонки.Добавить("Код");
ВстроенныеФункции.Индексы.Добавить("Имя, КоличествоПараметров");

ДобавитьВстроеннуюФункцию("СтрДлина", "StrLen", 1, 53);
ДобавитьВстроеннуюФункцию("СокрЛ", "TrimL", 1, 54);
ДобавитьВстроеннуюФункцию("СокрП", "TrimR", 1, 55);
ДобавитьВстроеннуюФункцию("СокрЛП", "TrimAll", 1, 56);
ДобавитьВстроеннуюФункцию("Лев", "Left", 2, 57);
ДобавитьВстроеннуюФункцию("Прав", "Right", 2, 58);
ДобавитьВстроеннуюФункцию("Сред", "Mid", 3, 59);
ДобавитьВстроеннуюФункцию("Найти", "Find", 2, 60);
ДобавитьВстроеннуюФункцию("Врег", "Upper", 1, 61);
ДобавитьВстроеннуюФункцию("Нрег", "Lower", 1, 62);
ДобавитьВстроеннуюФункцию("Символ", "Char", 1, 63);
ДобавитьВстроеннуюФункцию("КодСимвола", "CharCode", 2, 64);
ДобавитьВстроеннуюФункцию("ПустаяСтрока", "IsBlankString", 1, 65);
ДобавитьВстроеннуюФункцию("Цел", "Int", 1, 66);
ДобавитьВстроеннуюФункцию("Окр", "Round", 2, 67);
ДобавитьВстроеннуюФункцию("Окр", "Round", 3, 68);
ДобавитьВстроеннуюФункцию("Булево", "Boolean", 1, 69);
ДобавитьВстроеннуюФункцию("Число", "Number", 1, 70);
ДобавитьВстроеннуюФункцию("Строка", "String", 1, 71);
ДобавитьВстроеннуюФункцию("Дата", "Date", 1, 72);
ДобавитьВстроеннуюФункцию("Дата", "Date", 3, 73);
ДобавитьВстроеннуюФункцию("Дата", "Date", 6, 74);
ДобавитьВстроеннуюФункцию("ДобавитьМесяц", "AddMonth", 2, 75);
ДобавитьВстроеннуюФункцию("НачалоМесяца", "BegOfMonth", 1, 76);
ДобавитьВстроеннуюФункцию("КонецМесяца", "EndOfMonth", 1, 77);
ДобавитьВстроеннуюФункцию("НачалоКвартала", "BegOfQuarter", 1, 78);
ДобавитьВстроеннуюФункцию("КонецКвартала", "EndOfQuarter", 1, 79);
ДобавитьВстроеннуюФункцию("НачалоГода", "BegOfYear", 1, 80);
ДобавитьВстроеннуюФункцию("КонецГода", "EndOfYear", 1, 81);
ДобавитьВстроеннуюФункцию("Год", "Year", 1, 82);
ДобавитьВстроеннуюФункцию("Месяц", "Month", 1, 83);
ДобавитьВстроеннуюФункцию("День", "Day", 1, 84);
ДобавитьВстроеннуюФункцию("Час", "Hour", 1, 85);
ДобавитьВстроеннуюФункцию("Минута", "Minute", 1, 86);
ДобавитьВстроеннуюФункцию("Секунда", "Second", 1, 87);
ДобавитьВстроеннуюФункцию("ДеньГода", "DayOfYear", 1, 88);
ДобавитьВстроеннуюФункцию("НеделяГода", "WeekOfYear", 1, 89);
ДобавитьВстроеннуюФункцию("ДеньНедели", "WeekDay", 1, 90);
ДобавитьВстроеннуюФункцию("НачалоНедели", "BegOfWeek", 1, 91);
ДобавитьВстроеннуюФункцию("КонецНедели", "EndOfWeek", 1, 92);
ДобавитьВстроеннуюФункцию("НачалоДня", "BegOfDay", 1, 93);
ДобавитьВстроеннуюФункцию("КонецДня", "EndOfDay", 1, 94);
ДобавитьВстроеннуюФункцию("НачалоЧаса", "BegOfHour", 1, 95);
ДобавитьВстроеннуюФункцию("КонецЧаса", "EndOfHour", 1, 96);
ДобавитьВстроеннуюФункцию("НачалоМинуты", "BegOfMinute", 1, 97);
ДобавитьВстроеннуюФункцию("КонецМинуты", "EndOfMinute", 1, 98);
ДобавитьВстроеннуюФункцию("ТекущаяДата", "CurrentDate", 0, 99);
ДобавитьВстроеннуюФункцию("СтрЗаменить", "StrReplace", 3, 100);
ДобавитьВстроеннуюФункцию("СтрЧислоСтрок", "StrLineCount", 1, 101);
ДобавитьВстроеннуюФункцию("СтрПолучитьСтроку", "StrGetLine", 2, 102);
ДобавитьВстроеннуюФункцию("Мин", "Min", Неопределено, 103); // см. ПосетитьВыражениеИдентификатор()
ДобавитьВстроеннуюФункцию("Макс", "Max", Неопределено, 104); // см. ПосетитьВыражениеИдентификатор()
ДобавитьВстроеннуюФункцию("СтрЧислоВхождений", "StrOccurrenceCount", 2, 105);
ДобавитьВстроеннуюФункцию("ОписаниеОшибки", "ErrorDescription", 0, 106);
ДобавитьВстроеннуюФункцию("ТипЗнч", "TypeOf", 1, 107);
ДобавитьВстроеннуюФункцию("Тип", "Type", 1, 108);
ДобавитьВстроеннуюФункцию("Вычислить", "Eval", 1, 109);
ДобавитьВстроеннуюФункцию("Формат", "Format", 2, 110);
ДобавитьВстроеннуюФункцию("Новый", "New", 2, 111);
ДобавитьВстроеннуюФункцию("ACos", "ACos", 1, 112);
ДобавитьВстроеннуюФункцию("ASin", "ASin", 1, 113);
ДобавитьВстроеннуюФункцию("ATan", "ATan", 1, 114);
ДобавитьВстроеннуюФункцию("Cos", "Cos", 1, 115);
ДобавитьВстроеннуюФункцию("Exp", "Exp", 1, 116);
ДобавитьВстроеннуюФункцию("Log", "Log", 1, 117);
ДобавитьВстроеннуюФункцию("Log10", "Log10", 1, 118);
ДобавитьВстроеннуюФункцию("Pow", "Pow", 2, 119);
ДобавитьВстроеннуюФункцию("Sin", "Sin", 1, 120);
ДобавитьВстроеннуюФункцию("Sqrt", "Sqrt", 1, 121);
ДобавитьВстроеннуюФункцию("Tan", "Tan", 1, 122);
ДобавитьВстроеннуюФункцию("ТРег", "ТРег", 1, 127);
ДобавитьВстроеннуюФункцию("ИнформацияОбОшибке", "ErrorInfo", 0, 128);
